/*
 * SRMI (an easy-to-use zero-code remoting library for Java)
 * Copyright (C) 2007  Mohammad Nabil Hussein
 * mohammad (dot) nabil (dot) h (at) gmail (dot) com
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package SRMI.RemotingServices.ClassGeneration;

import SRMI.RemotingServices.ClassGeneration.Client.RmiStubAdapterGenerator;
import SRMI.SRMIException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * Generator for the pass by remote stub to support the pass by remote
 * feature.
 * @author meemo
 */
public class PassByRemoteStubGenerator extends ClassGenerator
{        
    /**     
     * @param wrapped The class to generated a pass by remote stub for.
     */
    public PassByRemoteStubGenerator(Class wrapped) throws SRMIException
    {        
        // if someone needs a pass-by-remote, they also gonna need a remote interface :)
        new RemoteInterfaceGenerator(wrapped).Prepare();
        
        
            // they will also need a client-side stub adapter
            new RmiStubAdapterGenerator(wrapped).Prepare();
        
        
        
        setWrappedClass(wrapped);
    }

    protected void PrepareEmit()
    {
        super.PrepareDefaultWrapperEmit();
    }

    protected void EmitSupers()
    {
        file.print("extends " + super.wrappedFullyQualifiedClassName + " implements java.io.Serializable");
    }

    protected void EmitConstructor() throws SRMIException
    {
        // default constructor ( server side ?? )
        file.print(String.format(
                    "\tpublic %s()\n" + 
                    "\t{\n" +
                    "\t}\n\n",
                super.generatedClassName));
        
        
            new RmiStubAdapterGenerator(getWrappedClass()).Prepare();
        
        //
        file.print(String.format(
                    "\t// client side code\n" +
                    "\tpublic %s(%s wrapped)\n" +
                    "\t{\n" +
                                
                String.format(
                "\t\t\tif(wrapped instanceof %s_pass_by_remote_stub)\n" +                
                "\t\t\t{\n" +
                "\t\t\t\tSystem.out.println(\"resolving remote-to-remote reference to remote-reference\");\n" +
                "\t\t\t\tthis.host = ((%s_pass_by_remote_stub)wrapped).host;\n" +
                "\t\t\t\tthis.uuid = ((%s_pass_by_remote_stub)wrapped).uuid;\n" +              
                "\t\t\t\treturn;\n" +
                "\t\t\t}\n\n",
                wrappedUnQualifiedClassName,wrappedUnQualifiedClassName,wrappedUnQualifiedClassName) +                
                                                
                String.format(
                "\t\t\tif(wrapped instanceof %s_clientside_rmistub_adapter)\n" +                
                "\t\t\t{\n" +
                "\t\t\t\tSystem.out.println(\"resolving remote-to-remote reference to remote-reference\");\n" +
                "\t\t\t\tthis.host = ((%s_clientside_rmistub_adapter)wrapped).host;\n" +
                "\t\t\t\tthis.uuid = ((%s_clientside_rmistub_adapter)wrapped).uuid;\n" +              
                "\t\t\t\treturn;\n" +
                "\t\t\t}\n\n",
                wrappedUnQualifiedClassName,wrappedUnQualifiedClassName,wrappedUnQualifiedClassName) +                
                
                    "\t\ttry\n" +
                    "\t\t{\n" +
                    "\t\t\t// The declare object method do not declare twice anyway, we just call it to make sure that the object is declared\n" +
                    "\t\t\tSRMI.RemotingServices.ServerUtils.DeclareObject(wrapped);\n" +
                    "\t\t\tuuid = SRMI.RemotingServices.ServerUtils.LookupUUID(wrapped);\n" +
                    "\t\t\thost = SRMI.RemotingServices.CommonUtils.detectExternalInterfaceIP();\n" +
                    "\t\t} catch (Exception ex) { ex.printStackTrace(); }\n" +
                    "\t}\n\n",
                super.generatedClassName,super.wrappedFullyQualifiedClassName,super.generatedClassName));
        
        file.print(String.format(
                    "\tprivate %s getObject()\n" +
                    "\t{\n" +
                    "\t\tif(stub != null)\n" +
                    "\t\t\treturn stub;\n" +
                    "\t\ttry\n" + 
                    "\t\t{\n" +
                    "\t\t\tstub = (%s) SRMI.RemotingServices.ClientUtils.getRemoteObject(%s.class,host,uuid);\n" +
                    "\t\t\treturn stub;\n" +
                    "\t\t} catch (Exception ex) { ex.printStackTrace(); }\n" +
                    "\t\treturn stub;\n"+
                    "\t}\n\n",
                super.wrappedFullyQualifiedClassName,super.wrappedFullyQualifiedClassName,super.wrappedFullyQualifiedClassName));
        
        // overriding .clone to solve the indirect remote reference problem
        file.print(String.format(
                    "\tpublic Object clone () throws CloneNotSupportedException\n" +
                    "\t{\n" +
                    "\t\t%s copy = new %s();\n" +
                    "\t\tcopy.host=this.host; // the original host\n" +
                    "\t\tcopy.uuid=this.uuid;\n" +
                    "\t\treturn copy; // copy.stub is meant to be null\n" +
                    "\t}\n\n",
                super.generatedClassName,super.generatedClassName
                ));
    }

    protected void EmitFunction(Method m)
    {
        int mod = m.getModifiers();
        if(!Modifier.isPrivate(mod)
        && !Modifier.isStatic(mod) 
        && !Modifier.isFinal(mod)        
        )
        {            
            EmitModifiers(mod);
            file.print(" ");
            EmitType(m.getReturnType());
            file.print(" ");
            file.print(m.getName());
            file.print("(");
            EmitArgs(m, false);
            file.print(")");
            EmitExceptions(m.getExceptionTypes());
            file.print("\n\t{\n");
            
            EmitWrappedCall(m);
            
            file.print("\n");            
            file.print("\t}\n\n");
        } 
    }
    
    private void EmitWrappedCall(Method m)
    {
        file.print(
                String.format (
                "\t\t%sgetObject().%s(",
                ( m.getReturnType().equals(java.lang.Void.TYPE)?"":"return "),
                m.getName( ) ) ) ;
        EmitArgs(m,true);
        file.print(");");
    }
    
    protected void EmitMembersVariables()
    {
        file.print(String.format(
                    "\tprivate java.util.UUID uuid;\n" +
                    "\tprivate String host;\n" +
                    "\tprivate transient %s stub;\n\n",
                super.wrappedFullyQualifiedClassName));
    }

    protected String getGeneratedClassNameSuffix()
    {
        return "_pass_by_remote_stub";
    }
}
